#include "comxfsys.h"

#include <QTcpSocket>
#include <QDateTime>
#include <QDebug>

#include <QJsonObject>
#include <QJsonArray>
#include <QJsonDocument>

#include "data/mycachedata.h"
#include "data/receivedata.h"
#include "data/myfunc.h"
#include "data/datadef.h"

//
int  ComXFSys::QSize = 100;

ComXFSys::ComXFSys(QObject *parent)
    : QThread(parent)
    , running(true)
    , client(NULL)
    , linking(false)
    , ativity(false)
    , initDataFlag(false)
    , _ReadData_over(0)
{
    ptr_MyCacheData = MyCacheData::getInstance();
    areaId = ptr_MyCacheData->getAreaId();
    areaType = ptr_MyCacheData->getAreaType();
    areaName = ptr_MyCacheData->getAreaName();
    areaDesc = ptr_MyCacheData->getAreaDesc();
    ptr_ReceiveData = ReceiveData::getInstance();

    ip = ptr_MyCacheData->getIpClientJson();
    port = ptr_MyCacheData->getPortClientJson();
    linkTime = getSec();
    heartTime = getSec();
}

ComXFSys::~ComXFSys()
{
    running=false;
    try{
        this->terminate();
        this->wait();
        qInfo() << "ComXFSys::destroy";
    }catch(...){
        qDebug() << "ComXFSys::destroy fail";
    }
}

void ComXFSys::run()
{
    initTcpSocket();
    qRegisterMetaType<QAbstractSocket::SocketError>("MySocketError");
    qInfo() << "ComXFSys::run and initTcpSocket";  
    while (running) {
        this->checklink();
        this->initWriteData();
        this->dataUncode();
        this->writeData();
        this->checkForReadyRead();
        this->msleep(10);
    }
    try{
        destroyTcpSocket();
        qInfo() << "ComXFSys::destroyTcpSocket";
    }catch(...){
        qDebug() << "ComXFSys::destroyTcpSocket fail";
    }
    exec();
}

bool ComXFSys::initTcpSocket()
{
    try{
        if(NULL==client){
            client = new QTcpSocket();
            connect(client,SIGNAL(readyRead()),this,SLOT(readData()));
//            connect(client, &QTcpSocket::disconnected, client, &QTcpSocket::deleteLater);
            connect(client,SIGNAL(error(QAbstractSocket::SocketError))
                    ,this,SLOT(sockeError(QAbstractSocket::SocketError)));
            return true;
        }
        return false;
    }catch(...){
        qDebug() << "ComXFSys::initTcpSocket Exception";
        return false;
    }
}

bool ComXFSys::destroyTcpSocket()
{
    try{
        if(NULL!=client){
            disconnect(client,SIGNAL(readyRead()),this,SLOT(readData()));
//            disconnect(client, &QTcpSocket::disconnected, client, &QTcpSocket::deleteLater);
            disconnect(client,SIGNAL(error(QAbstractSocket::SocketError))
                    ,this,SLOT(sockeError(QAbstractSocket::SocketError)));
            linking = !disconnectMyHost();
            delete client;
            client = NULL;
        }
    }catch(...){
        qDebug() << "ComXFSys::destroyTcpSocket() Exception";
        return false;
    }
    return true;
}

bool ComXFSys::connectMyHost()
{
    try{
        if(NULL==client){
            initTcpSocket();
            return false;
        }
        if(QAbstractSocket::UnconnectedState==client->state()){
            client->connectToHost(ip,port,QIODevice::ReadWrite);
            if(client->waitForConnected()){
                qInfo() << QString("ComXFSys,success connect to host(%1:%2)")
                            .arg(ip).arg(port);
                ativity = true;
                initDataFlag = true;
                return true;
            }else{
                qWarning() << QString("ComXFSys,connect to host(%1:%2) fail!")
                            .arg(ip).arg(port);
                return false;
            }
        }else if(QAbstractSocket::ConnectedState==client->state()){
            return true;
        }else{
            return false;
        }
    }catch(...){
        qDebug() << "ComXFSys::connectMyHost Exception";
        return false;
    }
}

bool ComXFSys::disconnectMyHost()
{
    try{
        if(NULL==client){
            return true;
        }
        if(QAbstractSocket::ConnectedState==client->state())
        {
            qInfo() << QString("disconnect to host(%1:%2)")
                        .arg(ip).arg(port);
            client->disconnectFromHost();
            ativity = false;
            initDataFlag = false;
            return client->waitForDisconnected();
        }else if(QAbstractSocket::UnconnectedState==client->state()){
            return true;
        }else{
            return false;
        }
    }catch(...){
        qDebug() << "ComXFSys::disconnectMyHost Exception";
        return true;
    }
}

void ComXFSys::checklink()
{
    try{
        if(!linking){
            if(linkTime< getSec()){
                linking = connectMyHost();
                linkTime = getSec()+10;
            }
        }else{
            if((heartTime+30)<getSec())
            {
                if(!ativity){
                    linking = !disconnectMyHost();
                }
                heartTime = getSec();
                ativity = false;
            }
        }
    }catch(...){
        qDebug() << "MySocketTran::checklink Exception";
    }
}

void ComXFSys::checkForReadyRead()
{
    if(NULL!=client&&linking)
    {
        client->waitForReadyRead(10);
    }
}

void ComXFSys::sockeError(QAbstractSocket::SocketError error)
{
    if(NULL==client)
        return;
    switch (error) {
    case QAbstractSocket::ConnectionRefusedError:
        break;
    case QAbstractSocket::RemoteHostClosedError:
        break;
    case QAbstractSocket::HostNotFoundError:
        break;
    case QAbstractSocket::SocketAccessError:
        break;
    case QAbstractSocket::SocketResourceError:
        break;
    case QAbstractSocket::SocketTimeoutError:
        return;
    case QAbstractSocket::DatagramTooLargeError:
        break;
    case QAbstractSocket::NetworkError:
        break;
    case QAbstractSocket::AddressInUseError:
        break;
    case QAbstractSocket::SocketAddressNotAvailableError:
        break;
    case QAbstractSocket::UnsupportedSocketOperationError:
        break;
    case QAbstractSocket::ProxyAuthenticationRequiredError:
        break;
    case QAbstractSocket::SslHandshakeFailedError:
        break;
    case QAbstractSocket::UnfinishedSocketOperationError:
        break;
    case QAbstractSocket::ProxyConnectionRefusedError:
        break;
    case QAbstractSocket::ProxyConnectionClosedError:
        break;
    case QAbstractSocket::ProxyConnectionTimeoutError:
        break;
    case QAbstractSocket::ProxyNotFoundError:
        break;
    case QAbstractSocket::ProxyProtocolError:
        break;
    case QAbstractSocket::OperationError:
        break;
    case QAbstractSocket::SslInternalError:
        break;
    case QAbstractSocket::SslInvalidUserDataError:
        break;
    case QAbstractSocket::TemporaryError:
        break;
    case QAbstractSocket::UnknownSocketError:
        break;
    default:
        break;
    }
    qDebug() << QString("socket(%1,%2) error:"+client->errorString())
                .arg(ip).arg(port);
    linking = !disconnectMyHost();
}

void ComXFSys::readData()
{
    if(client->bytesAvailable()>0)
    {
        QByteArray datagram;
        datagram.resize(client->bytesAvailable());
        client->read(datagram.data(),datagram.size());
        ativity = true;
        //在这里我们进行一个转换，之后就可以用QJson分析把我们收到的json数据
        QString str = QString::fromLocal8Bit(datagram.data());
        qWarning() << QString("readData:%1").arg(str);
        this->addRD(str);
    }
}

void ComXFSys::dataUncode()
{
    QString msg;
    if(this->getFirstRD(msg)){
        QJsonParseError json_error;
        QJsonDocument parse_doucment = QJsonDocument::fromJson(msg.toStdString().data(), &json_error);

        if(json_error.error == QJsonParseError::NoError)
        {
            if(parse_doucment.isObject())
            {
                QJsonObject obj = parse_doucment.object();
                long area_sn = 0;
                if(obj.contains("area_sn"))
                {
                    QJsonValue name_value = obj.take("area_sn");
                    if(name_value.isString())
                    {
                         QString str = QString::fromLocal8Bit(name_value.toString().toLocal8Bit().data());
//                         qInfo() <<QString("area_sn:%1").arg(str);
                         area_sn = str.toLong();
                         if(areaId!=area_sn)
                             return;
                    }
                }else{
                    return;
                }
                long dev_sn = 0;
                if(obj.contains("dev_sn"))
                {
                    QJsonValue name_value = obj.take("dev_sn");
                    if(name_value.isString())
                    {
                         QString str = QString::fromLocal8Bit(name_value.toString().toLocal8Bit().data());
//                         qInfo() <<QString("dev_sn:%1").arg(str);
                         dev_sn = str.toLong();
                    }
                }else{
                    return;
                }
                long point_sn = 0;
                if(obj.contains("point_sn"))
                {
                    QJsonValue name_value = obj.take("point_sn");
                    if(name_value.isString())
                    {
                         QString str = QString::fromLocal8Bit(name_value.toString().toLocal8Bit().data());
//                         qInfo() <<QString("ponit_sn:%1").arg(str);
                         point_sn = str.toLong();
                    }
                }else{
                    return;
                }
                float value = 0.0;
                if(obj.contains("value"))
                {
                    QJsonValue name_value = obj.take("value");
                    if(name_value.isString())
                    {
                         QString str = QString::fromLocal8Bit(name_value.toString().toLocal8Bit().data());
//                         qInfo() <<QString("value:%1").arg(str);
                         value = str.toFloat();
                    }
                }else{
                    return;
                }
                qInfo()<< QString("JsondataUncode and control(%1,%2,%3,%4)").arg(area_sn).arg(dev_sn).arg(point_sn).arg(value,0,'f',2);
                control(area_sn,dev_sn,point_sn,value);
            }
        }
        if(!this->removeFirstRD())
        {
            qWarning() << "ComXFSys::dataUncode removeFirstRD fail!";
        }
    }
//    else{
//        qWarning() << QString("ComXFSys:dataUncode exception!");
//    }
}

void ComXFSys::initWriteData()
{
    if(!initDataFlag)
        return;
    initDataFlag = false;
    if(NULL==client){
        return;
    }
    if(QAbstractSocket::ConnectedState!=client->state())
    {
        return;
    }
//    qInfo()<< "ComXFSys initWriteData";
    try{
        QList<DevClient> devs = ptr_MyCacheData->getDevInfo();
#ifdef _DEBUG
        qInfo()<< "ComXFSys::initWriteData";
        qInfo()<< QString("ComXFSys::initWriteData devs.size(%1)")
                   .arg(devs.size());
#endif
        QJsonObject json;
        json.insert("area_sn", QString("%1").arg(areaId));
        json.insert("area_name", QString("%1").arg(areaName));
        json.insert("area_type", QString("%1").arg(areaType));
        json.insert("area_desc", QString("%1").arg(areaDesc));
        //dev_list
        QJsonArray json_dev_l;

        for(QList<DevClient>::iterator itdev = devs.begin();
            itdev!=devs.end(); itdev++){
            //dev_obj
            QJsonObject json_dev;
            json_dev.insert("dev_sn", QString("%1").arg(itdev->devID));
            json_dev.insert("dev_name", QString("%1").arg(itdev->name));
            json_dev.insert("dev_type", QString("%1").arg((int)itdev->devType));
            json_dev.insert("dev_desc", QString("%1").arg(itdev->name));

            QList<PointClient> ps = ptr_MyCacheData->getPointInfo(itdev->devID);
            #ifdef _DEBUG
            qInfo()<< QString("ComXFSys initWriteData ps.size(%1,%2)")
                       .arg(itdev->devID).arg(ps.size());
            #endif
            QJsonArray json_p_l;
            for(QList<PointClient>::iterator itp = ps.begin();itp!=ps.end();itp++)
            {
                //p_obj
                QVariantMap objMap;
                objMap.insert("point_sn",QString("%1").arg(itp->pID));
                objMap.insert("point_type",QString("%1").arg((int)itp->pType));
                objMap.insert("point_name",itp->name);
                objMap.insert("point_desc",itp->name);
                QString dt_str = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz");
                objMap.insert("time",dt_str);
                objMap.insert("value",QString("%1").arg(itp->defval,0,'f',2));
                QJsonObject json_p = QJsonObject::fromVariantMap(objMap);
                json_p_l.append(json_p);
            }
            json_dev.insert("point", json_p_l);
            json_dev_l.append(json_dev);
        }
        json.insert("device",json_dev_l);
        QJsonDocument document;
        document.setObject(json);
//        QByteArray byte_array = document.toJson(QJsonDocument::Indented);
        QByteArray byte_array = document.toJson(QJsonDocument::Compact);
        int bl = byte_array.size();
        if(byte_array[bl-1]=='\n')   //判断最后一个字符是否为换行符“\n”
        {
            byte_array.resize(bl+1);                    //将QByteArray 长度加1
            byte_array[bl]='\r';                        //加上一个回车符“\r”
        }else{
            byte_array.resize(bl+2);
            byte_array[bl]='\n';
            byte_array[bl+1]='\r';
        }
        QString json_str(byte_array);
        #ifdef _DEBUG
        qInfo() << QString("JSON:%1").arg(json_str);
        #endif
        client->write(byte_array);
        bool ret = client->waitForBytesWritten();
        if (!ret) {
            qWarning() << QString("ComXFSys::initWriteData fail: %1.")
                        .arg(json_str);
        }else{
            ativity = true;
        }
    }catch(...){
        qDebug() << "ComXFSys initWriteData Exception";
    }
}

void ComXFSys::writeData()
{
    if(NULL==client){
        return;
    }
    if(QAbstractSocket::ConnectedState!=client->state())
    {
        return;
    }
//    qInfo()<< "ComXFSys writeData";
    QMap<int,QQueue<JsonPValue> > its;
    //（1）生成Json
    QJsonObject json;
    if (ptr_ReceiveData->getFirstWDYunS(its,10))
    {
        #ifdef _DEBUG
        qInfo()<< "writeData wdYun info";
        #endif
        json.insert("area_sn", QString("%1").arg(areaId));
        //dev_list
        QJsonArray json_dev_l;
        for(QMap<int,QQueue<JsonPValue> >::iterator it = its.begin();it!=its.end();it++)
        {
            //dev_obj
            QJsonObject json_dev;
            json_dev.insert("dev_sn", QString("%1").arg(it.key()));
            //ponit_list
            QJsonArray json_p_l;
            while (!it.value().isEmpty()) {
                JsonPValue jval = it.value().dequeue();
                //p_obj
                QVariantMap objMap;
                objMap.insert("point_sn",QString("%1").arg(jval.pID));
                QString dt_str = QDateTime::fromTime_t(jval.sec).addMSecs(jval.msec).toString("yyyy-MM-dd hh:mm:ss.zzz");
                objMap.insert("time",dt_str);
                objMap.insert("value",QString("%1").arg(jval.val,0,'f',2));
                QJsonObject json_p = QJsonObject::fromVariantMap(objMap);
                //insert point list
                json_p_l.append(json_p);
            }
            json_dev.insert("point", json_p_l);
            json_dev_l.append(json_dev);
        }
        json.insert("device",json_dev_l);

        QJsonDocument document;
        document.setObject(json);
        QByteArray byte_array = document.toJson(QJsonDocument::Compact);
//        QByteArray byte_array = document.toJson(QJsonDocument::Indented);

        int bl = byte_array.size();
        if(byte_array[bl-1]=='\n')   //判断最后一个字符是否为换行符“\n”
        {
            byte_array.resize(bl+1);                    //将QByteArray 长度加1
            byte_array[bl]='\r';                        //加上一个回车符“\r”
        }else{
            byte_array.resize(bl+2);
            byte_array[bl]='\n';
            byte_array[bl+1]='\r';
        }
        QString json_str(byte_array);
        #ifdef _DEBUG
        qInfo() << QString("JSON:%1").arg(json_str);
        #endif
        client->write(byte_array);
        bool ret = client->waitForBytesWritten();
        if (!ret) {
            qWarning() << QString("ComXFSys::writeData fail: %1.")
                        .arg(json_str);
        }else{
            ativity = true;
        }
    }
}

void ComXFSys::control(long areaId_,long devID, long pID, float val)
{
    if(areaId!=areaId_)
        return;
    PFromDev _pfrom;
    if (ptr_MyCacheData->getFromInfo(devID, (int)pID, _pfrom))
    {
        float _val = val;
        ptr_MyCacheData->getCValue(devID, static_cast<unsigned int>(pID), _val);
        if(OnYX==_pfrom.pType)
        {
            _val = _val > 0 ? float(1.0) : float(0.0);
        }
        RDS rds(OnSet,_pfrom.pID, _pfrom.pType, _val);
        ptr_ReceiveData->addRDS(rds);
        qInfo() << QString("setPValue from client to ip[%1],time(%2),devID(%3),pID(%4),val(%5),down_control")
                    .arg(_pfrom.ipStr).arg(PFunc::getCurrentTime())
                    .arg(devID).arg(pID).arg(val,0,'f',2);
    }
    else {
        //virtual point
        float _val = val;
        bool sendflag = false;
        bool recordflag = false;
        int  clientType = 1;
        ptr_MyCacheData->setValue(devID,static_cast<unsigned int>(pID),_val
            , sendflag, recordflag, clientType);
        qInfo() << QString("setPValue from client,time(%1),devID(%2),pID(%3),val(%4),down_control")
                    .arg(PFunc::getCurrentTime())
                    .arg(devID).arg(pID).arg(val,0,'f',2);
//        qint64 _usec = mycache->getUsec();
//        //client
//        if (sendflag)
//        {
//            WDC _wdlc(static_cast<unsigned long long>(devID), pID
//                , static_cast<unsigned int>(_usec/1000)
//                , static_cast<unsigned int>(_usec%1000), _val, clientType);
//            m_ReceiveData->addWDLC(_wdlc);
////            if (mycache->getMcsClientFunc()) {
////                m_ReceiveData->addWDLS(_wdlc);
////            }
//        }
    }
}

qint64 ComXFSys::getSec()
{
    return QDateTime::currentSecsSinceEpoch();
}

qint64 ComXFSys::getMSec()
{
    return QDateTime::currentMSecsSinceEpoch();
}


//
int ComXFSys::getRDSize()
{
    int size = 0;
    m_MutexRD.lock();
    size = ReadData.size();
    m_MutexRD.unlock();
    return size;
}

bool ComXFSys::isEmptyRD()
{
    bool ret = false;
    m_MutexRD.lock();
    ret = ReadData.empty();
    m_MutexRD.unlock();
    return ret;
}

bool ComXFSys::getFirstRD(QString &it) {
    if (!isEmptyRD()) {
        m_MutexRD.lock();
        it = ReadData.front();
        m_MutexRD.unlock();
        return true;
    }
    else {
        return false;
    }
}

bool ComXFSys::removeFirstRD() {
    if (!isEmptyRD()) {
        m_MutexRD.lock();
        ReadData.pop_front();
        m_MutexRD.unlock();
        return true;
    }
    else {
        return false;
    }
}

void ComXFSys::addRD(QString it) {
    if (getRDSize() > QSize) {
        _ReadData_over++;
        if (_ReadData_over >= 10) {//每溢出10次，报告一次
            _ReadData_over = 0;
            qWarning() << QString("the size of ComXFSys::ReadData vector is up to limmit size: %1 .")
                        .arg(QSize);
        }
        removeFirstRD();
    }
    m_MutexRD.lock();
    ReadData.append(it);
    m_MutexRD.unlock();
}
/////////////////////////
